package com.gmcloud.common.log.aspect;

import cn.hutool.core.text.CharSequenceUtil;
import com.gmcloud.common.core.exception.BaseException;
import com.gmcloud.common.core.utils.SpringContextHolder;
import com.gmcloud.common.log.enums.LogTypeEnum;
import com.gmcloud.common.log.event.SysLogEvent;
import com.gmcloud.common.log.util.SysLogUtils;
import com.gmcloud.common.utils.JsonUtil;
import com.gmcloud.upms.api.system.entity.SysLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.EvaluationContext;

/**
 * @author zl.sir
 * @version 1.0
 * @since 2022/8/18 12:36
 * 操作日志使用spring event异步入库
 */
@Aspect
public class SysLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(SysLogAspect.class);

    @Around("@annotation(sysLog)")
    public Object around(ProceedingJoinPoint point, com.gmcloud.common.log.annotation.SysLog sysLog) {
        String strClassName = point.getTarget().getClass().getName();
        String strMethodName = point.getSignature().getName();

        String value = sysLog.value();
        String expression = sysLog.expression();
        // 当前表达式存在 SPEL，会覆盖 value 的值
        if (CharSequenceUtil.isNotBlank(expression)) {
            // 解析SPEL
            MethodSignature signature = (MethodSignature) point.getSignature();
            EvaluationContext context = SysLogUtils.getContext(point.getArgs(), signature.getMethod());
            try {
                value = SysLogUtils.getValue(context, expression, String.class);
            } catch (Exception e) {
                // SPEL 表达式异常，获取 value 的值
                LOGGER.error("@SysLog 解析SPEL {} 异常", expression);
            }
        }

        SysLog logVo = SysLogUtils.getSysLog();
        logVo.setTitle(value);
        logVo.setBody(JsonUtil.objToStringPretty(point.getArgs()));

        // 发送异步日志事件
        Long startTime = System.currentTimeMillis();
        Object obj;

        try {
            obj = point.proceed();
        } catch (Exception e) {
            logVo.setType(LogTypeEnum.ERROR.getType());
            logVo.setException(e.getMessage());
            throw new BaseException(e);
        } catch (Throwable e) {
            throw new BaseException(e);
        } finally {
            Long endTime = System.currentTimeMillis();
            logVo.setTime(endTime - startTime);

            SpringContextHolder.publishEvent(new SysLogEvent(logVo));
        }

        if (LOGGER.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder();
            sb.append("\n【请求 URL】：").append(logVo.getRequestUri());
            sb.append("\n【http请求方法】：").append(logVo.getMethod());
            sb.append("\n【请求 IP】：").append(logVo.getRemoteAddr());
            sb.append("\n【请求类名】：").append(strClassName);
            sb.append("\n【请求方法名】：").append(strMethodName);
            sb.append("\n【body】：").append(logVo.getBody());
            sb.append("\n【请求参数】：").append(logVo.getParams());
            LOGGER.debug("{}", sb);
        }
        return obj;
    }
}
